package org.suyafei.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.suyafei.component.PayFactory;
import org.suyafei.config.RabbitMQConfig;
import org.suyafei.constant.TimeConstant;
import org.suyafei.cotroller.request.ConfirmOrderRequest;
import org.suyafei.cotroller.request.ProductOrderPageRequest;
import org.suyafei.enums.*;
import org.suyafei.exception.BizException;
import org.suyafei.interceptor.LoginInterceptor;
import org.suyafei.manager.ProductManager;
import org.suyafei.manager.ProductOrderManager;
import org.suyafei.model.EventMessage;
import org.suyafei.model.LoginUser;
import org.suyafei.model.ProductDO;
import org.suyafei.model.ProductOrderDO;
import org.suyafei.service.ProductOrderService;
import org.suyafei.utils.CommonUtil;
import org.suyafei.utils.JsonData;
import org.suyafei.utils.JsonUtil;
import org.suyafei.vo.PayInfoVO;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author 宿某人
 * @Date: 2022/02/06/4:04 下午
 * @Description:
 */
@Service
@Slf4j
public class ProductOrderServiceImpl implements ProductOrderService {

    @Autowired
    private ProductOrderManager productOrderManager;

    @Autowired
    private ProductManager productManager;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private PayFactory payFactory;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Override

    public Map<String, Object> page(ProductOrderPageRequest request) {
        Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        Map<String, Object> pageMap = productOrderManager.page(request.getPage(), request.getSize(), accountNo, request.getState());
        return pageMap;
    }

    @Override
    public String queryProductOrderState(String outTradeNo) {
        Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);
        return productOrderDO == null ? "" : productOrderDO.getState();
    }

    /**
     * 防重提交（TODO）
     * 获取最新的流量包价格
     * 订单验价
     * 如果有优惠券或者其他抵扣
     * 验证前端显示和后台计算价格
     * 创建订单对象保存数据库
     * 发送延迟消息-用于自动关单（TODO）
     * 创建支付信息-对接三方支付（TODO）
     * 回调更新订单状态（TODO）
     * 支付成功创建流量包（TODO）
     *
     * @param orderRequest
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public JsonData confirmOrder(ConfirmOrderRequest orderRequest) {
        LoginUser loginUser = LoginInterceptor.threadLocal.get();

        //生成订单号
        String orderOutTradeNo = CommonUtil.getStringNumRandom(32);
        ProductDO productDO = productManager.findDetailById(orderRequest.getProductId());

        //验证价格
        this.chekPrice(productDO, orderRequest);

        //创建订单
        ProductOrderDO productOrderDO = this.saveProductOrder(orderRequest, loginUser, orderOutTradeNo, productDO);

        //创建支付对象
        PayInfoVO payInfoVO = PayInfoVO.builder().outTradeNo(orderOutTradeNo)
                .accountNo(loginUser.getAccountNo())
                .clientType(orderRequest.getClientType())
                .payType(orderRequest.getPayType())
                .title(productDO.getTitle())
                .description("")
                .payFee(orderRequest.getPayAmount())
                .orderPayTimeoutMills(TimeConstant.ORDER_PAY_TIMEOUT_MILLS).build();

        //调用支付信息 TODO
        String codeUrl = payFactory.pay(payInfoVO);
        //发送延迟消息 TODO
        EventMessage eventMessage = EventMessage.builder()
                .eventMessageType(EventMessageTypeEnum.PRODUCT_ORDER_NEW.name())
                .accountNo(loginUser.getAccountNo())
                .bizId(orderOutTradeNo)
                .build();

        rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(), rabbitMQConfig.getOrderCloseDelayRoutingKey(), eventMessage);
        Map<String, String> map = new HashMap<>();
        map.put("out_trade_no", productOrderDO.getOutTradeNo());
        map.put("code_url", codeUrl);
        return JsonData.buildSuccess(map);
    }

    /**
     * 处理订单相关消息
     * @param eventMessage
     */
    @Override
    public void handleProductOderMessage(EventMessage eventMessage) {
        String messageType = eventMessage.getEventMessageType();

        try {
            if (EventMessageTypeEnum.PRODUCT_ORDER_NEW.name().equalsIgnoreCase(messageType)) {
                this.closeProductOrder(eventMessage);
            } else if (EventMessageTypeEnum.PRODUCT_ORDER_PAY.name().equalsIgnoreCase(messageType)) {
                //订单已经支付，更新订单状态
                String outTradeNo = eventMessage.getBizId();
                Long accountNo = eventMessage.getAccountNo();
                int rows = productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
                log.info("订单更新成功：rows={}", rows);
            }

        } catch (Exception e) {
            log.error("订单消费者消费失败{}", eventMessage);
            throw new BizException(BizCodeEnum.MQ_CONSUME_EXCEPTION);
        }

    }

    /**
     * * //延迟消息的时间 需要比订单过期 时间长一点，这样就不存在查询的时候，用户还能支付成功
     * *
     * * //查询订单是否存在，如果已经支付则正常结束
     * * //如果订单未支付，主动调用第三方支付平台查询订单状态
     * *     //确认未支付，本地取消订单
     * *     //如果第三方平台已经支付，主动的把订单状态改成已支付，造成该原因的情况可能是支付通道回调有问题，然后触发支付后的动作，如何触发？RPC还是？
     *
     * @param eventMessage
     * @return
     */
    @Override
    public boolean closeProductOrder(EventMessage eventMessage) {
        String outTradeNo = eventMessage.getBizId();
        Long accountNo = eventMessage.getAccountNo();

        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);

        if (productOrderDO == null) {
            log.warn("订单不存在");
            return true;
        }

        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())) {
            //已经支付
            log.info("直接确认消息，订单已经支付{}", eventMessage);
            return true;
        }
        //未支付，需要向第三方查询支付平台查询状态
        if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())) {
            PayInfoVO.builder().payType(productOrderDO.getPayType())
                    .outTradeNo(outTradeNo)
                    .accountNo(accountNo);

            //TODO 需要向第三方查询
            String payResult = "";

            if (StringUtils.isBlank(payResult)) {
                //如果为空，则未支付，本地取消订单
                productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.CANCEL.name(), productOrderDO.getState());
                log.info("未支付成功，本地取消订单：{}", eventMessage);
            } else {
                //支付成功，主动吧订单状态更新成支付
                log.warn("支付成功，但是微信回调通知失败，需要排查问题");
                productOrderManager.updateOrderPayState(outTradeNo, accountNo, ProductOrderStateEnum.PAY.name(), productOrderDO.getState());
                //触发支付成功的逻辑，TODO
            }

        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public JsonData processOrderCallbackMsg(ProductOrderPayTypeEnum payType, Map<String, String> paramsMap) {

        //订单哈
        String outTradeNo = paramsMap.get("out_trade_no");
        //交易状态
        String tradeState = paramsMap.get("trade_state");

        Long accountNo = Long.valueOf(paramsMap.get("account_no"));

        ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);

        Map<String, Object> content = new HashMap<>(4);
        content.put("outTradeNo", outTradeNo);
        content.put("buyNum", productOrderDO.getBuyNum());
        content.put("accountNo", accountNo);
        content.put("product", productOrderDO.getProductSnapshot());


        EventMessage eventMessage = EventMessage.builder()
                .bizId(outTradeNo)
                .accountNo(accountNo)
                .messageId(outTradeNo)
                .content(JsonUtil.obj2Json(content))
                .eventMessageType(EventMessageTypeEnum.PRODUCT_ORDER_PAY.name())
                .build();

        if (ProductOrderPayTypeEnum.ALI_PAY.name().equalsIgnoreCase(payType.name())) {

        } else if (ProductOrderPayTypeEnum.WECHAT_PAY.name().equalsIgnoreCase(payType.name())) {
            if ("SUCCESS".equalsIgnoreCase(tradeState)) {
                /**
                 * 问题1：如果redis保存成但是异步消息发送失败，redis不会回滚消息，
                 * 可以利用mysql处理。
                 */
                Boolean flag = redisTemplate.opsForValue().setIfAbsent(outTradeNo, "ok", 3, TimeUnit.DAYS);
                if (flag) {
                    rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(),
                            rabbitMQConfig.getOrderUpdateTrafficRoutingKey(), eventMessage);
                    return JsonData.buildSuccess();
                }


            }
        }
        return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);
    }

    /**
     * 创建订单
     *
     * @param loginUser
     * @param orderOutTradeNo
     * @param productDO
     */
    private ProductOrderDO saveProductOrder(ConfirmOrderRequest orderRequest, LoginUser loginUser, String orderOutTradeNo, ProductDO productDO) {

        ProductOrderDO productOrderDO = ProductOrderDO.builder().accountNo(loginUser.getAccountNo())
                //用户信息
                .nickname(loginUser.getUsername())
                .productId(productDO.getId())
                .productTitle(productDO.getTitle())
                .productSnapshot(JsonUtil.obj2Json(productDO))
                .productAmount(productDO.getAmount())

                //订单信息
                .buyNum(orderRequest.getBuyNum())
                .outTradeNo(orderOutTradeNo)
                .createTime(new Date())
                .del(0)

                //发票信息
                .billType(BillTypeEnum.valueOf(orderRequest.getBillType()).name())
                .billHeader(orderRequest.getBillHeader())
                .billReceiverPhone(orderRequest.getBillReceiverPhone())
                .billReceiverEmail(orderRequest.getBillReceiverEmail())
                .billContent(orderRequest.getBillContent())

                //实际支付的总价格
                .payAmount(orderRequest.getPayAmount())
                //总价，未使用优惠券或者折扣的价格
                .totalAmount(orderRequest.getTotalAmount())
                .state(ProductOrderStateEnum.NEW.name())
                //支付类型
                .payType(ProductOrderPayTypeEnum.valueOf(orderRequest.getPayType()).name()).build();
        productOrderManager.add(productOrderDO);
        return productOrderDO;
    }

    /**
     * 验证前后端价格是否一致
     *
     * @param productDO
     * @param orderRequest
     */
    private void chekPrice(ProductDO productDO, ConfirmOrderRequest orderRequest) {

        //后端计算价格
        BigDecimal bizTotal = BigDecimal.valueOf(orderRequest.getBuyNum()).multiply(productDO.getAmount());
        //验证前后端价格是否一直，如果有优惠券也要计算
        if (bizTotal.compareTo(orderRequest.getPayAmount()) != 0) {
            log.error("验证价格失败{}", orderRequest);
            throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
        }

    }
}
